package message

import (
	"bytes"
	"encoding/binary"
)

type Request struct {
	HeadLength uint32
	BodyLength uint32
	RequestId  uint32
	Version    uint8
	Compresser uint8
	Serializer uint8

	ServiceName string
	MethodName  string
	// meta 的 key value 最好都设计成 string 类型，不要用 any，后续处理 any 很麻烦
	Meta map[string]string

	Data []byte
}

func EncodeReq(req *Request) []byte {
	req.CalculateHeadLength()
	req.CalculateBodyLength()

	bs := make([]byte, req.HeadLength+req.BodyLength)
	// 写入 head 长度数值
	binary.BigEndian.PutUint32(bs[:4], req.HeadLength)
	// 写入 body 长度数值
	binary.BigEndian.PutUint32(bs[4:8], req.BodyLength)
	// 写入 RequestId 值
	binary.BigEndian.PutUint32(bs[8:12], req.RequestId)
	// 写入 Version 值
	bs[12] = req.Version
	// 写入 Compresser 值
	bs[13] = req.Compresser
	// 写入 Serializer 值
	bs[14] = req.Serializer

	data := bs[15:]
	// 写入 ServiceName 值
	copy(data[:len(req.ServiceName)], req.ServiceName)
	// 写入分隔符
	data[len(req.ServiceName)] = '\n'

	data = data[len(req.ServiceName)+1:]
	// 写入 MethodName 值
	copy(data[:len(req.MethodName)], req.MethodName)
	// 写入分隔符
	data[len(req.MethodName)] = '\n'

	data = data[len(req.MethodName)+1:]
	// 写入 Meta，key-value 整体之间用 \n 分开，key 和 value 之间用 \r 分开
	if req.Meta != nil {
		for key, value := range req.Meta {
			copy(data[:len(key)], key)
			data[len(key)] = '\r'
			data = data[len(key)+1:]
			copy(data[:len(value)], value)
			data[len(value)] = '\n'
			data = data[len(value)+1:]
		}
	}

	// 写入 data
	copy(data, req.Data)

	return bs
}

func DecodeReq(data []byte) *Request {
	req := &Request{}
	req.HeadLength = binary.BigEndian.Uint32(data[:4])
	req.BodyLength = binary.BigEndian.Uint32(data[4:8])
	req.RequestId = binary.BigEndian.Uint32(data[8:12])
	req.Version = uint8(data[12])
	req.Compresser = uint8(data[13])
	req.Serializer = uint8(data[14])

	data = data[15:]

	index := bytes.IndexByte(data, '\n')
	req.ServiceName = string(data[:index])
	data = data[index+1:]

	index = bytes.IndexByte(data, '\n')
	req.MethodName = string(data[:index])
	data = data[index+1:]

	indexR := bytes.IndexByte(data, '\r')
	if indexR > -1 {
		req.Meta = make(map[string]string, 6)
		for indexR > -1 {
			index = bytes.IndexByte(data, '\n')
			req.Meta[string(data[:indexR])] = string(data[indexR+1 : index])
			data = data[index+1:]
			indexR = bytes.IndexByte(data, '\r')
		}
	}

	if len(data) > 0 {
		req.Data = data
	}

	return req
}

func (req *Request) CalculateHeadLength() {
	length := 15 + len(req.ServiceName) + 1 + len(req.MethodName) + 1
	for key, val := range req.Meta {
		length += len(key)
		length++
		length += len(val)
		length++
	}
	req.HeadLength = uint32(length)
}
func (req *Request) CalculateBodyLength() {
	req.BodyLength = uint32(len(req.Data))
}
